/*******************************************************************************
 * Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
 * 
 * This file is part of the OpenWGA server platform.
 * 
 * OpenWGA is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * In addition, a special exception is granted by the copyright holders
 * of OpenWGA called "OpenWGA plugin exception". You should have received
 * a copy of this exception along with OpenWGA in file COPYING.
 * If not, see <http://www.openwga.com/gpl-plugin-exception>.
 * 
 * OpenWGA is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with OpenWGA in file COPYING.
 * If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package de.innovationgate.webgate.api.templates;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathException;

import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGBackendException;
import de.innovationgate.webgate.api.WGColumnSet;
import de.innovationgate.webgate.api.WGContent;
import de.innovationgate.webgate.api.WGDatabaseRevision;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGDocumentCore;
import de.innovationgate.webgate.api.WGExpressionException;
import de.innovationgate.webgate.api.WGExtensionDataContainer;
import de.innovationgate.webgate.api.WGFileDerivateMetaData;
import de.innovationgate.webgate.api.WGFileMetaData;
import de.innovationgate.webgate.api.WGIllegalArgumentException;
import de.innovationgate.webgate.api.WGNotSupportedException;
import de.innovationgate.webgate.api.WGRelationData;
import de.innovationgate.webgate.api.WGSessionContext;
import de.innovationgate.webgate.api.WGSystemException;
import de.innovationgate.webgate.api.WGUpdateLog;
import de.innovationgate.webgate.api.fake.WGFakeDocument;

/**
 * A wrapper object that takes a JavaBean storage object and serves it's data as document core.
 * Is used by SimpleContentSource.
 */
public class BeanWrapper extends Object implements WGDocumentCore {
	
	


    private boolean _temporary;
	private boolean _lowerCaseItems;
	private boolean _saved;
	private WGContent _doc;
	private boolean _deleted = false;
	private ArrayList _items;
	Object _bean = null;
	BeanKey _key = null;
	String _fakeLanguage = null;
	protected SimpleContentSource _db = null;
	JXPathContext _context = null;

	
	/**
	 * Constructor. Not to be used outside WGAPI.
	 * @param db
	 * @param key
	 * @param bean
	 * @param saved
	 * @param temporary
	 */
	public BeanWrapper(SimpleContentSource db, BeanKey key, Object bean, boolean saved, boolean temporary) {
		_key = key;
		_bean = bean;
		_db = db;
		_fakeLanguage = db.getFakeLanguage();
		_context = JXPathContext.newContext(_bean);
		_saved = saved;
		_temporary = temporary;
		_lowerCaseItems = db.getSpecs().isLowerCaseItems();
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getType()
	 */
	public int getType() {
		return WGDocument.TYPE_CONTENT;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getFastAccessKey()
	 */
	public Object getFastAccessKey() {
		return _key;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#isDeleted()
	 */
	public boolean isDeleted() {
		return _deleted;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#isTemporary()
	 */
	public boolean isTemporary() {
		return _temporary;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#hasItem(java.lang.String)
	 */
	public boolean hasItem(String strName) {
		
		// B000049F6
		if (strName != null && isLowerCaseItems()) {
			strName = strName.toLowerCase();
		}
		
		return getItemNames().contains(strName);
		
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getItemValue(java.lang.String)
	 */
	public Object getItemValue(String strName) {
		
		if (strName != null && isLowerCaseItems()) {
			strName = strName.toLowerCase();
		}
		
		try {
			return _context.getValue(strName);
		}
		catch (org.apache.commons.jxpath.JXPathException e) {
			return null;
		}
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getMetaData(java.lang.String)
	 */
	public Object getMetaData(String type) throws WGSystemException, WGIllegalArgumentException {
		
		if (type.equals(WGContent.META_STRUCTENTRY)) {
			return _key;
		}
        
        // See if we should return meta properties anyway. If not always return default
        if (!_db.getSpecs().isServePropertiesAsMetas()) {
            return WGFakeDocument.getMetaDataDefault(WGDocument.TYPE_CONTENT, type, _fakeLanguage);            
        }
		
		// Try to retrieve meta from bean. Else get default
		try {
			return _context.getValue(type.toLowerCase());
		}
		catch (JXPathException e) {
			return WGFakeDocument.getMetaDataDefault(WGDocument.TYPE_CONTENT, type, _fakeLanguage);					
		}
	}



	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getFileNames()
	 */
	public List getFileNames() {
		return null;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getFileData(java.lang.String)
	 */
	public InputStream getFileData(String strFile) {
		return null;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getFileSize(java.lang.String)
	 */
	public int getFileSize(String strFile) {
		return 0;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getCreated()
	 */
	public Date getCreated() throws WGSystemException, WGIllegalArgumentException {
		return (Date) getMetaData(WGDocument.META_CREATED);
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getLastModified()
	 */
	public Date getLastModified() throws WGSystemException, WGIllegalArgumentException {
		return (Date) getMetaData(WGDocument.META_LASTMODIFIED);
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#setItemValue(java.lang.String, java.lang.Object)
	 */
	public boolean setItemValue(String strName, Object value) {

		if (strName != null && _lowerCaseItems) {
			strName = strName.toLowerCase();
		}

		try {
			_context.setValue(strName, value);
			return true;
		}
		catch (org.apache.commons.jxpath.JXPathException e) {
			return false;
		}
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#setMetaData(java.lang.String, java.lang.Object)
	 */
	public boolean setMetaData(String name, Object value) {
        
        // See if we should return meta properties anyway. Cancel if not.
        if (!_db.getSpecs().isServePropertiesAsMetas()) {
            return false;            
        }
        
		try {
			_context.setValue(name, value);
			return true;
		}
		catch (JXPathException e) {
			return false;
		}

	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#save()
	 */
	public WGDatabaseRevision save(java.util.Date lastModified) throws WGAPIException {
		
		Object key;
		if (_db.getSpecs().isCalculatesKeys()) {
			_key = new BeanKey(_key.getFolder(), _db.calculateKey(_key.getFolder(), _bean));
		}
		
		boolean result;
		if (isSaved()) {
			result = _db.updateContent(_key.getFolder(), _key.getKey(), _bean);
		}
		else {
			result = _db.insertContent(_key.getFolder(), _key.getKey(), _bean);
		}
        
		if (!result) {
		    throw new WGBackendException("Saving document " + _doc.getContentKey() + " failed");
		}
		return null;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#evaluateExpression(java.lang.String)
	 */
	public Object evaluateExpression(String expression) throws WGExpressionException {
		return null;
	}





	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#setWGDocument(de.innovationgate.webgate.api.WGDocument)
	 */
	public void setWGDocument(WGDocument doc) {
		_doc = (WGContent) doc;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#dispose()
	 */
	public void dispose() {
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getItemNames()
	 */
	public List getItemNames() {

		if (_items == null) {
			Method[] methods = _bean.getClass().getMethods();
			_items = new ArrayList();
			Method method;
			for (int i = 0; i < methods.length; i++) {
				method = methods[i];
				String methodName = method.getName();
                if (isLowerCaseItems()) {
                    methodName = methodName.toLowerCase();
                }
                
				if (methodName.startsWith("get")) {
					_items.add(methodName.substring(3));
				}
				else if (methodName.startsWith("is")) {
					_items.add(methodName.substring(2));
				}
			}
		}
		return _items;

	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#removeItem(java.lang.String)
	 */
	public boolean removeItem(String Name) {
		return false;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#isDataCacheable()
	 */
	public boolean isDataCacheable() {
		return true;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#attachFile(java.io.File)
	 */
	public boolean attachFile(File file) {
		return false;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#removeFile(java.lang.String)
	 */
	public boolean removeFile(String name) {
		return false;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#remove()
	 */
	public WGDatabaseRevision remove() throws WGAPIException {
		_db.removeContent(_key.getFolder(), _key.getKey());
		if(_doc != null) {
			// Struct entry must also be deleted bc. it would remain cached otherwise
			WGSessionContext sessionContext = _doc.getDatabase().getSessionContext();
			boolean cascadeDeletions = sessionContext.isCascadeDeletions();
			sessionContext.setCascadeDeletions(false);
			_doc.getStructEntry().remove();
			sessionContext.setCascadeDeletions(true);			
		}
		_deleted = true;
		return null;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#isSaved()
	 */
	public boolean isSaved() {
		return _saved;
	}

	/* (Kein Javadoc)
	 * @see de.innovationgate.webgate.api.WGDocumentCore#getNativeObject()
	 */
	public Object getNativeObject() {
		return _bean;
	}

	protected boolean isLowerCaseItems() {
		return _lowerCaseItems;
	}
	
    public String getOriginDatabase() {
        return _db.getDb().getDbReference();
    }

	public void renameFile(String oldFileName, String newFileName)
			throws WGAPIException {
		throw new WGNotSupportedException("renameFile() is not supported on this document implementation.");		
	}
	
	public WGFileMetaData getFileMetaData(String strFile) throws WGAPIException {
		throw new WGNotSupportedException("getFileMetaData() is not supported on this document implementation.");
	}
	
    public WGDocumentCore getRelation(String name) throws WGAPIException {
        throw new WGNotSupportedException("Content Relations are not supported");
    }

    public WGDocumentCore removeRelation(String name) throws WGAPIException {
        throw new WGNotSupportedException("Content Relations are not supported");
    }

    public WGDocumentCore setRelation(String name, WGDocumentCore target) throws WGAPIException {
        throw new WGNotSupportedException("Content Relations are not supported");
    }
    
    
    public List<String> getRelationNames() throws WGAPIException {
        throw new WGNotSupportedException("Content Relations are not supported");
    }

    public boolean hasFileMetadata() throws WGAPIException {
        return false;
    }

    public boolean hasFile(String file) throws WGBackendException {
        return false;
    }

    public WGRelationData getRelationData(String name) throws WGAPIException {
        throw new WGNotSupportedException("Content relations are not supported");
    }

    public WGDocumentCore setRelation(WGRelationData data) throws WGAPIException {
        throw new WGNotSupportedException("Content relations are not supported");
    }

    public Object getExtensionData(String strName) throws WGAPIException {
        throw new WGNotSupportedException("Attributes are not supported");    }

    public List getExtensionDataNames() throws WGAPIException {
        throw new WGNotSupportedException("Attributes are not supported");    }

    public void removeExtensionData(String strName) throws WGAPIException {
        throw new WGNotSupportedException("Attributes are not supported");    }

    public void writeExtensionData(String strName, Object value) throws WGAPIException {
        throw new WGNotSupportedException("Attributes are not supported");
    }

    public List<String> getRelationNamesOfGroup(String group, WGColumnSet order) throws WGBackendException {
        throw new WGNotSupportedException("Content relations are not supported");
    }

    public WGExtensionDataContainer retrieveFileExtensionDataHandler(String strFile) throws WGAPIException {
        return null;
    }
    
    @Override
    public List<WGFileDerivateMetaData> getFileDerivates(String strFile) throws WGAPIException {
        return null;
    }
    
    @Override
    public void markFileMetaDataModified(WGFileMetaData md) throws WGAPIException {
    }
    
    @Override
    public WGFileDerivateMetaData createFileDerivate(String originalFileName, String creator, String derivateName, InputStream in, Map<String,Object> customMdFields) throws WGAPIException {
        throw new WGNotSupportedException("File derivates are not supported on this database");
    }
    
    @Override
    public InputStream getFileDerivateData(String id) {
        return null;
    }
    
    @Override
    public WGFileDerivateMetaData getFileDerivateMetaData(String id) {
        return null;
    }
    
    @Override
    public void writeFileDerivateMetaData(WGFileDerivateMetaData md) throws WGAPIException {
        throw new WGNotSupportedException("File derivates are not supported on this database");
    }
    
    @Override
    public void removeFileDerivate(String id) throws WGAPIException {
        throw new WGNotSupportedException("File derivates are not supported on this database");   
    }
    
    @Override
    public Iterator<WGUpdateLog> getLastUpdates() throws WGAPIException {
        return Collections.<WGUpdateLog>emptyList().iterator();
    }

    

}
